/*
 * Copyright (C) 2025 ameise <ameise.wang@gmail.com> - All Rights Reserved
 *
 * This file is part of e3time.
 *
 * e3time is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * e3time is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with e3time. If not, see <https://www.gnu.org/licenses/>.
 */

package e3time

import (
	"container/list"
	"gitee.com/ameise84/e3ds/heap"
	"gitee.com/ameise84/e3lock"
	"gitee.com/ameise84/e3pool/go_pool"
	"math"
	"time"
)

func newTimerHolder(phd TimerHolder) *holder {
	hd := &holder{
		hp: heap.NewMin[uint64, *timer](),
	}
	hd.doTr = phd.NewTimer(hd, nil, time.Hour)
	return hd
}

type holder struct {
	mu         e3lock.SpinLock
	hp         heap.Heap[uint64, *timer]
	nextFireAt Time
	doTr       actuator
	wait       list.List //kill
}

func (ts *holder) NewTimer(cb TimerHandler, ctx any, dur Duration) Timer {
	fireAt := Now().Add(dur)
	tr := takeTimer(ts, cb, ctx, fireAt, dur, 1)
	ts.add(tr)
	return tr
}

func (ts *holder) NewTimerWithDoTime(cb TimerHandler, ctx any, fireAt Time) Timer {
	tr := takeTimer(ts, cb, ctx, fireAt, math.MaxInt, 1)
	ts.add(tr)
	return tr
}

func (ts *holder) NewTick(cb TimerHandler, ctx any, dur Duration, repeat int) Timer {
	fireAt := Now().Add(dur)
	tr := takeTimer(ts, cb, ctx, fireAt, dur, repeat)
	ts.add(tr)
	return tr
}

func (ts *holder) NewTickWithFirstAt(cb TimerHandler, ctx any, fireAt Time, dur Duration, repeat int) Timer {
	tr := takeTimer(ts, cb, ctx, fireAt, dur, repeat)
	ts.add(tr)
	return tr
}

func (ts *holder) KillTimerWithID(id uint64) {
	ts.killWithID(id)
}

func (ts *holder) KillTimer(tr Timer) {
	switch ntr := tr.(type) {
	case *timer:
		ts.killWithID(ntr.UniqueID())
	default:
		panic("invalid timer type")
	}
}

func (ts *holder) KillAllTimers() {
	ts.killAll()
}

func (ts *holder) add(tr *timer) {
	ts.mu.Lock()
	defer ts.mu.Unlock()
	_, _ = ts.hp.Push(tr)
	ts.checkResetActuator(tr)
}

func (ts *holder) modify(tr *timer, fireAt Time, dur Duration, repeat int) bool {
	ts.mu.Lock()
	defer ts.mu.Unlock()
	tr.fireAt = fireAt
	tr.dur = dur
	tr.repeat = repeat
	_, ok := ts.hp.Find(tr.UniqueID())
	if ok {
		ts.hp.Update(tr.UniqueID(), tr)
	} else {
		_, _ = ts.hp.Push(tr)
	}
	ts.checkResetActuator(tr)
	return true
}

func (ts *holder) pause(tr *timer) {
	ts.mu.Lock()
	defer ts.mu.Unlock()
	ts.hp.Remove(tr.UniqueID())
}

func (ts *holder) killWithID(id uint64) {
	ts.mu.Lock()
	defer ts.mu.Unlock()
	if tr, ok := ts.hp.Remove(id); ok {
		if tr.isInTrigger.Load() {
			ts.wait.PushBack(tr)
		} else {
			freeTimer(tr)
		}
	}
}

func (ts *holder) killAll() {
	ts.mu.Lock()
	defer ts.mu.Unlock()
	timers := ts.hp.CleanToSlice()
	for _, tr := range timers {
		if tr.isInTrigger.Load() {
			ts.wait.PushBack(tr)
		} else {
			freeTimer(tr)
		}
	}
}

func (ts *holder) checkResetActuator(tr *timer) {
	top, _ := ts.hp.Peek()
	if top.UniqueID() == tr.UniqueID() && !tr.fireAt.Equal(ts.nextFireAt) {
		ts.nextFireAt = tr.fireAt
		ts.doTr.Reset(top.fireAt.Sub(Now()))
	}
}

func (ts *holder) OnTimer(_ Timer, fireAt time.Time) {
	events := [maxEvt]event{}
	idx := 0
	ts.mu.Lock()
	for {
		top, err := ts.hp.Peek()
		if err != nil {
			if ts.wait.Len() > 0 {
				ts.doTr.Reset(time.Second)
			}
			break
		}

		if idx == maxEvt || fireAt.Before(top.fireAt) {
			ts.nextFireAt = top.fireAt
			ts.doTr.Reset(top.fireAt.Sub(fireAt))
			if idx == maxEvt {
				_gLogger.Warn("timer on trigger is too more").Println() //通常由于服务器无法快速处理,对消息做一定延迟
			}
			break
		}

		if top.fire(&events[idx], fireAt) {
			ts.hp.Update(top.UniqueID(), top)
		} else {
			_, _ = ts.hp.Pop()
		}
		idx++
	}

	for {
		e := ts.wait.Front()
		if e == nil {
			break
		}
		ktr := e.Value.(*timer)
		if ktr.isInTrigger.Load() {
			break
		}
		freeTimer(ktr)
		ts.wait.Remove(e)
	}
	ts.mu.Unlock()
	for i := 0; i < idx; i++ {
		events[i].fireTimer()
	}
}

func (ts *holder) onGenTimer(args ...any) {
	now := args[0].(time.Time)
	ts.OnTimer(nil, now)
}

type holderGen struct {
	*holder
	tr             *time.Timer
	runner         go_pool.GoRunner
	timerRunner    go_pool.GoRunner
	fastForwardDur time.Duration
}

func (ts *holderGen) OnPanic(err error) {
	_gLogger.Error("OnPanic").Err(err).Println()
}

func (ts *holderGen) Reset(dur Duration, repeat ...int) (Time, bool) {
	_ = ts.tr.Reset(dur)
	return zeroTime, true
}

func (ts *holderGen) ResetFireAt(fire Time, _ ...int) bool {
	ts.tr.Reset(fire.Sub(Now()))
	return true
}

func (ts *holderGen) loop(...any) {
	for {
		select {
		case t := <-ts.tr.C:
			ts.mu.Lock()
			nowTime := t.Add(ts.fastForwardDur)
			ts.mu.Unlock()
			_ = ts.timerRunner.AsyncRun(ts.holder.onGenTimer, nowTime)
		}
	}
}

func (ts *holderGen) setFastForwardDuration(d time.Duration) {
	ts.mu.Lock()
	ts.fastForwardDur = d
	ts.mu.Unlock()
}
